package com.xcity.game.shop.peak;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.alibaba.fastjson.TypeReference;
import com.xcity.db.entity.Config;
import com.xcity.db.entity.Player;
import com.xcity.db.mapper.ConfigMapper;
import com.xcity.game.common.Cause;
import com.xcity.game.common.Currency;
import com.xcity.game.common.GameStaticConfig;
import com.xcity.game.common.MessageConst;
import com.xcity.game.common.PlayerPool;
import com.xcity.game.shop.ShopException;
import com.xcity.game.shop.ShopHandler;
import com.xcity.game.shop.ShopType;
import com.xcity.pomelo.push.msg.BatchItemsPush;
import com.xcity.pomelo.request.ShopBuyV2Request;
import com.xcity.pomelo.request.ShopRefreshV2Request;
import com.xcity.pomelo.response.ShopBuyV2Response;
import com.xcity.pomelo.response.ShopRefreshV2Response;
import com.xcity.pomelo.response.ShopV2Response.ShopItemV2;
import com.xcity.pomelo.response.ShopV2Response.ShopV2;
import com.xcity.util.DBUtils;

import naga.x.App;
import naga.x.game.template.TemplateService;
import naga.x.game.time.TimeUpdater;
import naga.x.net.Session;

/**
 * 巅峰银行
 * @author yang.li
 *
 */
public class PeakShopHandler implements ShopHandler {

	protected List<PeakShopTemplate> templates;
//	protected TIntObjectMap<PeakShopTemplate> templatesMap;
	protected Map<String, PeakShopTemplate> uniqueMap; // key: uniqueId
	private long lastRefreshTime;
	
	public PeakShopHandler() {
		templates = App.getApp().getServiceManager().get(TemplateService.class).getTemplatesList(PeakShopTemplate.class);
//		templatesMap = App.getApp().getServiceManager().get(TemplateService.class).getTemplates(PeakShopTemplate.class);
		uniqueMap = new HashMap<String, PeakShopTemplate>(templates.size());
		for (int i = 0; i < templates.size(); i++) {
			PeakShopTemplate template = templates.get(i);
			uniqueMap.put(template.getUniqueId(), template);
		}
		Config config = DBUtils.find(Config.KEY_PEAK_SHOP_LAST_REFRESH_TIME, ConfigMapper.class);
		if (config == null) {
			config = new Config(Config.KEY_PEAK_SHOP_LAST_REFRESH_TIME, TimeUpdater.getInstance().now());
			DBUtils.save(config, ConfigMapper.class);
		}
		lastRefreshTime = config.getLongValue();
	}
	
	@Override
	public void unRegister() {
		DBUtils.update(new Config(Config.KEY_PEAK_SHOP_LAST_REFRESH_TIME, getLastRefreshTime()), ConfigMapper.class);
	}
	
	/**
	 * 获取刷新cd（秒）
	 * @return
	 */
	public int getRefreshCD() {
		long millis = getLastRefreshTime() + GameStaticConfig.peakShopRefreshCD * 1000 - TimeUpdater.getInstance().now();
		return (int) (millis / 1000);
	}
	
	public long getLastRefreshTime() {
		long elapse = TimeUpdater.getInstance().now() - lastRefreshTime;
		int cd = GameStaticConfig.peakShopRefreshCD * 1000;
		if (elapse >= cd) { // dirty, need fix the time
			lastRefreshTime += elapse / cd * cd;
		}
		return lastRefreshTime;
	}
	
	/**
	 * 获取本次刷新消耗钻石
	 * @param player
	 * @return
	 */
	public int getRefreshCost(Player player) {
		int c = getRefreshCount(player);
		return GameStaticConfig.peakShopRefreshCost + GameStaticConfig.peakShopRefreshCostDelta * c;
	}
	
	/**
	 * 获取今日已用钻石刷新次数
	 * @param player
	 * @return
	 */
	public int getRefreshCount(Player player) {
		int lastDay = player.getPool().getIntValue(PlayerPool.PROPERTY_PEAK_SHOP_LAST_MANUAL_REFRESH_DAY);
		if (lastDay != TimeUpdater.getInstance().today()) {
			return 0;
		}
		return player.getPool().getIntValue(PlayerPool.PROPERTY_PEAK_SHOP_REFRESH_NUM);
	}
	
	/**
	 * 获取今日剩余钻石刷新次数
	 * @param player
	 * @return
	 */
	public int getLeftRefreshCount(Player player) {
		int n = GameStaticConfig.peakShopRefreshTimes - getRefreshCount(player);
		return n > 0 ? n : 0;
	}
	
	/**
	 * 累计钻石刷新次数
	 * @param player
	 * @param n
	 */
	public void addManualRefreshCount(Player player, int n) {
		int c = player.getPool().getIntValue(PlayerPool.PROPERTY_PEAK_SHOP_REFRESH_NUM);
		int lastDay = player.getPool().getIntValue(PlayerPool.PROPERTY_PEAK_SHOP_LAST_MANUAL_REFRESH_DAY);
		if (lastDay != TimeUpdater.getInstance().today()) { // reset
			player.poolSet(PlayerPool.PROPERTY_PEAK_SHOP_LAST_MANUAL_REFRESH_DAY, TimeUpdater.getInstance().today());
			c = 0;
		}
		player.poolRemove(PlayerPool.PROPERTY_PEAK_SHOP_BOUGHT_RECORD);
		player.poolSet(PlayerPool.PROPERTY_PEAK_SHOP_REFRESH_NUM, c + n);
	}
	
	/**
	 * 尝试执行系统自动刷新
	 * @param player
	 */
	public void tryAutoRefresh(Player player) {
		long lastTime = player.getPool().getLongValue(PlayerPool.PROPERTY_PEAK_SHOP_LAST_SYS_REFRESH_TIME);
		long lastSystemRefreshTime = getLastRefreshTime();
		if (lastTime < lastSystemRefreshTime) {
			player.poolSet(PlayerPool.PROPERTY_PEAK_SHOP_LAST_SYS_REFRESH_TIME, TimeUpdater.getInstance().now());
			player.poolRemove(PlayerPool.PROPERTY_PEAK_SHOP_BOUGHT_RECORD);
		}
	}
	
	static TypeReference<LinkedHashSet<String>> recordType = new TypeReference<LinkedHashSet<String>>() {};
	/**
	 * 记录购买行为
	 * @param player
	 * @param item
	 */
	public void recordBought(Player player, PeakShopTemplate item) {
		tryAutoRefresh(player);
		Set<String> set = player.getPool().getObject(PlayerPool.PROPERTY_PEAK_SHOP_BOUGHT_RECORD, recordType);
		if (set == null) {
			set = new LinkedHashSet<>();
			player.poolSet(PlayerPool.PROPERTY_PEAK_SHOP_BOUGHT_RECORD, set);
		}
		set.add(item.getUniqueId());
	}
	
	/**
	 * 检测是否已购买过该物品
	 * @param player
	 * @param itemId
	 * @return
	 */
	public boolean hasBought(Player player, PeakShopTemplate item) {
		Set<String> set = getBoughtRecord(player);
		return set != null && set.contains(item.getUniqueId());
	}
	
	public Set<String> getBoughtRecord(Player player) {
		tryAutoRefresh(player);
		Set<String> set = player.getPool().getObject(PlayerPool.PROPERTY_PEAK_SHOP_BOUGHT_RECORD, recordType);
		return set;
	}
	
	@Override
	public ShopV2 requestShop(Player player) {
		ShopV2 shop = new ShopV2();
		shop.type = ShopType.PEAK;
		shop.cd = getRefreshCD();
		shop.refreshCost = getRefreshCost(player);
		shop.refreshCount = getLeftRefreshCount(player);
		shop.refreshTotal = GameStaticConfig.peakShopRefreshTimes;
		if (ShopType.PEAK.visible) {
			shop.items = new ArrayList<ShopItemV2>(templates.size());
			for (int i = 0; i < templates.size(); i++) {
				PeakShopTemplate template = templates.get(i);
				int availableCount = hasBought(player, template) ? 0 : template.getCount();
				ShopItemV2 item = new ShopItemV2(template, Currency.PEAK_COIN, availableCount);
				shop.items.add(item);
			}
		}
		return shop;
	}
	
	@Override
	public ShopBuyV2Response requestBuy(Session session, ShopBuyV2Request req) throws ShopException {
		PeakShopTemplate template = req.getId() != null ? uniqueMap.get(req.getId()) : null;
		if (template == null) {
			throw new ShopException(MessageConst.SHOP_ITEM_NOT_EXIST, ShopException.TYPE_ITEM_NOT_FOUND);
		}
		Player player = session.getClient();
		if (hasBought(player, template)) {
			throw new ShopException(MessageConst.SHOP_ITEM_SOLD_OUT, ShopException.TYPE_ITEM_SOLD_OUT);
		}
		if (player.getBag().isFull()) {
			throw new ShopException(MessageConst.BAG_FULL);
		}
		if (!player.decPeakCoin(template.getPrice(), Cause.PEAK_SHOP_BUY)) {
			throw new ShopException(MessageConst.PEAK_COIN_NOT_ENOUGH);
		}
		player.getBag().addItemWithoutException(template.getItemTemplate(), template.getCount(), Cause.PEAK_SHOP_BUY);
		recordBought(player, template);
		
		ShopBuyV2Response res = new ShopBuyV2Response(req.getSerial(), ShopType.PEAK, template.getUniqueId(), 0);
		session.send(new BatchItemsPush(template.getItemTemplate(), template.getCount()));
		return res;
	}
	
	@Override
	public ShopRefreshV2Response requestRefresh(Session session, ShopRefreshV2Request req) throws ShopException {
		Player player = session.getClient();
		if (getLeftRefreshCount(player) <= 0) {
			throw new ShopException(MessageConst.SHOP_REFRESH_NUM_MAXED);
		}
		Set<String> boughtRecord = getBoughtRecord(player);
		if (boughtRecord == null || boughtRecord.isEmpty()) {
			throw new ShopException(MessageConst.SHOP_NO_NEED_REFRESH);
		}
		int cost = getRefreshCost(player);
		if (!player.costStone(cost, Cause.PEAK_SHOP_REFRESH)) {
			throw new ShopException(MessageConst.STONE_NOT_ENOUTH);
		}
		addManualRefreshCount(player, 1);
		
		ShopRefreshV2Response res = new ShopRefreshV2Response(req.getSerial());
		res.type = ShopType.PEAK;
		res.cd = getRefreshCD();
		res.refreshCost = getRefreshCost(player);
		res.refreshCount = getLeftRefreshCount(player);
		res.refreshTotal = GameStaticConfig.peakShopRefreshTimes;
		res.items = new ArrayList<ShopItemV2>(boughtRecord.size());
		for (String uniqueId : boughtRecord) {
			PeakShopTemplate template = uniqueMap.get(uniqueId);
			if (template != null) { // maybe removed from templates
				ShopItemV2 item = new ShopItemV2(template, Currency.PEAK_COIN, template.getCount());
				res.items.add(item);
			}
		}
		return res;
	}

}
